/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.formmetadata.service;

import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.web.common.customexception.WebCustomException;
import com.inspur.edp.web.common.environment.ExecuteEnvironment;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.formmetadata.api.FormMetadataCommonService;
import com.inspur.edp.web.formmetadata.api.dto.FormRelateMetadataInDesignParameterDto;
import com.inspur.edp.web.formmetadata.api.dto.FormRelateMetadataInDesignResultDto;
import com.inspur.edp.web.formmetadata.api.dto.RelateMetadataTypeEnum;
import com.inspur.edp.web.formmetadata.api.entity.FormSuInfoEntity;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContent;
import com.inspur.edp.web.formmetadata.metadata.FormMetadataContentService;
import com.inspur.edp.web.formmetadata.metadata.formdom.FormDOM;
import com.inspur.edp.web.formmetadata.metadataanalysis.CommandsAnalysis;
import com.inspur.edp.web.formmetadata.metadatamanager.MetadataManagerParameter;
import com.inspur.edp.web.formmetadata.metadatamanager.StateMachineMetadataManager;
import io.iec.edp.caf.boot.context.CAFContext;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.core.session.CafSession;

import java.util.*;
import java.util.concurrent.*;

/**
 * 表单关联元数据获取服务
 *
 * @author guozhiqi
 */
public class FormRelateMetadataService {
    /**
     * 获取表单元数据及其关联元数据
     *
     * @param parameter
     */
    public List<FormRelateMetadataInDesignResultDto> getFormRelateMetadata(FormRelateMetadataInDesignParameterDto parameter) {

        CafSession currentSession = CAFContext.current.getCurrentSession();
        // 定义返回值列表结构 线程安全的集合结构  每个方法执行都增加了synchronized
        List<FormRelateMetadataInDesignResultDto> safeFormRelateMetadataInDesignResultList = Collections.synchronizedList(new ArrayList<>(20));

        // 线程安全的CompletableFuture   List 每个方法的执行都增加了 synchronized
        List<CompletableFuture<?>> safeCompletableFutureList = Collections.synchronizedList(new ArrayList<>(20));
        // 使用线程池进行并发元数据读取
        Map<String, FormRelateMetadataInDesignResultDto> formRelateMetadataInDesignResultDtoConcurrentHashMap = new ConcurrentHashMap<>(32);

        // 使用线程安全的集合进行元数据的并发读取
        this.executeFormAndRelateMetadata(currentSession, parameter.getEnvironment(), parameter.getFormMetadataFileName(),
                parameter.getFormMetadataPath(), parameter.getFormMetadataId(), formRelateMetadataInDesignResultDtoConcurrentHashMap, safeCompletableFutureList);

        // 等待所有子任务执行完毕
        CompletableFuture<Void> combineCompletableFuture = CompletableFuture.allOf(safeCompletableFutureList.toArray(new CompletableFuture[0]));
        try {
            combineCompletableFuture.get();
        } catch (ExecutionException | InterruptedException e) {
            throw new WebCustomException("", e);
        }

        safeFormRelateMetadataInDesignResultList.addAll(formRelateMetadataInDesignResultDtoConcurrentHashMap.values());

        return safeFormRelateMetadataInDesignResultList;
    }

    /**
     * 定义表单元数据异步任务的返回结果
     */
    static class FormMetadataAsyncResult {
        public GspMetadata formMetadata;
        public FormDOM formDOM;

        public FormMetadataAsyncResult(GspMetadata formMetadata, FormDOM formDOM) {
            this.formMetadata = formMetadata;
            this.formDOM = formDOM;
        }

        public static FormMetadataAsyncResult init(GspMetadata formMetadata, FormDOM formDOM) {
            return new FormMetadataAsyncResult(formMetadata, formDOM);
        }
    }

    /**
     * 获取表单元数据及其关联的元数据信息
     *
     * @param executeEnvironment
     * @param formMetadataFileName
     * @param formMetadataFilePath
     * @param formMetadataId
     * @param safeCompletableFutureList 异步任务集合
     */
    private CompletableFuture<?> executeFormAndRelateMetadata(CafSession cafSession, ExecuteEnvironment executeEnvironment, String formMetadataFileName, String formMetadataFilePath, String formMetadataId,
                                                              Map<String, FormRelateMetadataInDesignResultDto> formRelateMetadataInDesignResultDtoConcurrentHashMap,
                                                              List<CompletableFuture<?>> safeCompletableFutureList) {
        long start = System.currentTimeMillis();
        GspMetadata formMetadata = getFormMetadata(executeEnvironment, formMetadataFileName, formMetadataFilePath, formMetadataId);
        if (formMetadata == null) {
            return null;
        }

        FormDOM formDOM = FormMetadataContentService.getInstance().getFormContent((FormMetadataContent) formMetadata.getContent());
        FormMetadataAsyncResult formMetadataAsyncResult = FormMetadataAsyncResult.init(formMetadata, formDOM);

        WebLogger.Instance.error("获取表单元数据耗时" + (System.currentTimeMillis() - start) + "ms");

        if (formMetadataAsyncResult.formDOM == null || formMetadataAsyncResult.formMetadata == null) {
            return null;
        }
        // 获取su info信息的异步请求
        CompletableFuture<Void> suInfoEntityCompletableFuture = CompletableFuture.supplyAsync(() -> getFormSuInfoEntity(formMetadataAsyncResult.formMetadata.getHeader().getBizobjectID())).thenApply(t -> {
            FormRelateMetadataInDesignResultDto formMetadataResultDto = new FormRelateMetadataInDesignResultDto(RelateMetadataTypeEnum.Form,
                    formMetadataAsyncResult.formMetadata.getHeader().getName(), formMetadataAsyncResult.formMetadata.getHeader().getFileName(), formMetadataAsyncResult.formMetadata.getHeader().getId(),
                    formMetadataAsyncResult.formMetadata.getContent(), formMetadataAsyncResult.formMetadata.getRelativePath(), t);
            formMetadataResultDto.setRelateFormMetadataId(formMetadata.getHeader().getId());
            formRelateMetadataInDesignResultDtoConcurrentHashMap.put(formMetadataAsyncResult.formMetadata.getHeader().getId(), formMetadataResultDto);
            return null;
        });
        safeCompletableFutureList.add(suInfoEntityCompletableFuture);

        // 获取关联的状态机元数据
        executeStateMachineWithFuture(cafSession, executeEnvironment, formMetadataAsyncResult.formMetadata, formMetadataAsyncResult.formDOM, formRelateMetadataInDesignResultDtoConcurrentHashMap, safeCompletableFutureList);

        // 获取关联的命令元数据
        executeCommandWithFuture(cafSession, executeEnvironment, formMetadataAsyncResult.formDOM, formMetadata, formRelateMetadataInDesignResultDtoConcurrentHashMap, safeCompletableFutureList);

        // 获取其关联的组合表单--元数据 提前获取
        if (formMetadataAsyncResult.formDOM.getModule().getExternalComponents() != null && !formMetadataAsyncResult.formDOM.getModule().getExternalComponents().isEmpty()) {
            formMetadataAsyncResult.formDOM.getModule().getExternalComponents().forEach(t -> {
                String metadataUri = StringUtility.getOrDefault(t.get("uri"), "");
                String metadataFileName = StringUtility.getOrDefault(t.get("fileName"), "");
                String metadataFilePath = StringUtility.getOrDefault(t.get("filePath"), "");
                String metadataNameSpace = StringUtility.getOrDefault(t.get("nameSpace"), "");
                this.executeFormAndRelateMetadata(cafSession, executeEnvironment, metadataFileName, metadataFilePath, metadataUri, formRelateMetadataInDesignResultDtoConcurrentHashMap, safeCompletableFutureList);
            });
        }
        return null;
    }

    /**
     * 异步执行命令元数据读取
     *
     * @param executeEnvironment
     * @param formDOM
     * @param formRelateMetadataInDesignResultDtoConcurrentHashMap
     * @param completableFutureList
     */
    private void executeCommandWithFuture(CafSession cafSession, ExecuteEnvironment executeEnvironment, FormDOM formDOM, GspMetadata formMetadata, Map<String, FormRelateMetadataInDesignResultDto> formRelateMetadataInDesignResultDtoConcurrentHashMap,
                                          List<CompletableFuture<?>> completableFutureList) {
        long start = System.currentTimeMillis();
        CommandsAnalysis commandsAnalysis = new CommandsAnalysis(executeEnvironment, false, false);
        Map<String, CommandsAnalysis.WebComponentMetadataAndExtra> webComponentMetadataHashMap = new ConcurrentHashMap<>();
        commandsAnalysis.resolveCommandAsync(CommandsAnalysis.ResolveCommandAsyncParameter.init(cafSession, formDOM, formMetadata), webComponentMetadataHashMap, completableFutureList, formRelateMetadataInDesignResultDtoConcurrentHashMap);
        WebLogger.Instance.info("获取命令元数据耗时 " + (System.currentTimeMillis() - start) + " ms");
    }

    private void executeStateMachineWithFuture(CafSession cafSession, ExecuteEnvironment executeEnvironment, GspMetadata formMetadata, FormDOM formDOM,
                                               Map<String, FormRelateMetadataInDesignResultDto> formRelateMetadataInDesignResultDtoConcurrentHashMap,
                                               List<CompletableFuture<?>> completableFutureList) {
        List<HashMap<String, Object>> stateMachineList = formDOM.getModule().getStateMachines();
        if (stateMachineList != null && !stateMachineList.isEmpty()) {
            HashMap<String, Object> firstStateMachine = stateMachineList.get(0);
            Object smObjUri = firstStateMachine.get("uri");
            if (smObjUri != null) {
                CompletableFuture<Void> stateMachineCompletableFuture = CompletableFuture.supplyAsync(() -> {
                    CAFContext.current.getService().setCurrentThreadPoolSession(cafSession);
                    long start = System.currentTimeMillis();
                    StateMachineMetadataManager stateMachineMetadataManager = new StateMachineMetadataManager(executeEnvironment, false, formMetadata.getRelativePath());
                    MetadataManagerParameter metadataManagerParameter = MetadataManagerParameter.init(smObjUri.toString(),
                            StringUtility.getOrDefaultEmpty(firstStateMachine.get("code")),
                            StringUtility.getOrDefaultEmpty(firstStateMachine.get("name")),
                            StringUtility.getOrDefaultEmpty(firstStateMachine.get("nameSpace")));
                    GspMetadata stateMachineMetadata = stateMachineMetadataManager.getStateMachine(metadataManagerParameter);
                    WebLogger.Instance.info("获取状态机元数据耗时 " + (System.currentTimeMillis() - start) + " ms");
                    return stateMachineMetadata;
                }).thenApply(t -> {
                    FormRelateMetadataInDesignResultDto stateMachineMetadataResultDto = new FormRelateMetadataInDesignResultDto(RelateMetadataTypeEnum.StateMachine, t.getHeader().getName(), t.getHeader().getFileName(),
                            t.getHeader().getId(), t.getContent(), t.getRelativePath());
                    stateMachineMetadataResultDto.setRelateFormMetadataId(formMetadata.getHeader().getId());
                    formRelateMetadataInDesignResultDtoConcurrentHashMap.put(t.getHeader().getId(), stateMachineMetadataResultDto);
                    return null;
                });
                completableFutureList.add(stateMachineCompletableFuture);
            }
        }
    }

    /**
     * 获取对应业务对象信息
     *
     * @param bizObjectId 业务对象ID
     * @return
     */
    private FormSuInfoEntity getFormSuInfoEntity(String bizObjectId) {
        if (StringUtility.isNullOrEmpty(bizObjectId)) {
            return null;
        }
        long start = System.currentTimeMillis();
        FormMetadataCommonService formMetadataCommonService = SpringBeanUtils.getBean(FormMetadataCommonService.class);
        FormSuInfoEntity formSuInfoEntity = formMetadataCommonService.getSuInfoWithBizobjId(bizObjectId);
        WebLogger.Instance.info("获取suInfo 耗时 " + (System.currentTimeMillis() - start) + " ms");
        return formSuInfoEntity;
    }

    /**
     * 依据运行环境进行元数据读取
     *
     * @param executeEnvironment
     * @param formMetadataFileName
     * @param formMetadataPath
     * @param formMetadataId
     * @return
     */
    private GspMetadata getFormMetadata(ExecuteEnvironment executeEnvironment, String formMetadataFileName, String formMetadataPath, String formMetadataId) {
        if (executeEnvironment.equals(ExecuteEnvironment.Design)) {
            return MetadataUtility.getInstance().getMetadataWithDesign(formMetadataFileName, formMetadataPath);
        }
        return MetadataUtility.getInstance().getMetadataInRuntime(formMetadataId);
    }
}
